package com.bosssoft.cloud.wechat.common.infra.handler;

import com.bosssoft.cloud.wechat.common.bean.sms.ISendSmsDTO;
import com.bosssoft.cloud.wechat.common.bean.sms.SDCZSendSmsDTO;
import com.bosssoft.cloud.wechat.common.bean.sms.SmsDTO;
import com.bosssoft.cloud.wechat.common.bean.sms.ZWXSendSmsDTO;
import com.bosssoft.cloud.wechat.common.infra.properties.EipsManager;
import com.bosssoft.cloud.wechat.infrastructure.constant.EipsConfigConsts;
import com.bosssoft.cloud.wechat.infrastructure.enums.ErrEnum;
import com.bosssoft.cloud.wechat.infrastructure.exception.TPSException;
import com.bosssoft.cloud.wechat.infrastructure.util.Assert;
import com.bosssoft.cloud.wechat.infrastructure.util.EBeanUtil;
import com.bosssoft.cloud.wechat.infrastructure.util.ReflectUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 短信handler类
 *
 * @author wcy create on 2021/6/29 14:04 下午
 */
@Slf4j
@Component
public class SmsHandler {

    // 验证码位数
    private static final Integer CODE_LENGTH = 4;
    // 验证码有效期(ps:生命周期到使用过or过期结束)
    private static final Integer CODE_LIFECYCLE = 5;

    @Resource
    private EipsManager eipsManager;
    @Resource
    private RedisTemplate<Object, Object> redisTemplate;

    /**
     * 发送手机验证码
     *
     * @param phone 手机号
     * @return 发送情况
     */
    public void send(String phone) {
        // 若有配置中心，则每次发送前，要重新获得url等信息（短信开发平台配置可能改变）
        SmsDTO smsDTO = eipsManager.getSms();
        // 短信验证码(4位)
        String randomNumber = RandomStringUtils.randomNumeric(CODE_LENGTH);
        log.info("手机短信验证码：{}，开始发送...", randomNumber);
        // 验证码记入redis（无论短信平台是否发送成功，都记录验证码）
        redisTemplate.opsForValue().set(phone, randomNumber, CODE_LIFECYCLE, TimeUnit.MINUTES);
        // 要发送的内容（根据当前配置获得）
        ISendSmsDTO sendSmsDTO = getSendSmsDTO(smsDTO, phone, randomNumber);
        // 由于短信开放平台还在使用NameValuePair方式传参，故这么做
        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost post = new HttpPost(smsDTO.getUrl());
        List<NameValuePair> pairs = getNameValuePairs(sendSmsDTO);
        post.setEntity(new UrlEncodedFormEntity(pairs, Charset.forName("gb2312")));
        try {
            CloseableHttpResponse execute = client.execute(post);
            StatusLine statusLine = execute.getStatusLine();
            // 如果返回状态码不是200
            if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
                throw new TPSException(
                        ErrEnum.C0501,
                        "短信平台返回状态：" + statusLine.toString()
                );
            }
        } catch (IOException e) {
            throw new TPSException(
                    ErrEnum.C0501,
                    String.format("短信平台信息:%s,发送参数:%s", smsDTO.toString(), sendSmsDTO.toString())
            );
        }
        log.info("发送成功");
    }

    /**
     * 手机验证码校验
     *
     * @param phone 手机号
     * @param code  随机验证码
     */
    public void verifyCode(String phone, String code) {
        String o = (String) redisTemplate.opsForValue().get(phone);
        Assert.isTrue(o != null && o.equals(code), ErrEnum.A0131);
        removeRandomCode(phone);
    }

    /**
     * 删除验证码
     *
     * @param phone 手机号
     */
    public void removeRandomCode(String phone) {
        redisTemplate.delete(phone);
    }

    /**
     * 获得要发送的请求内容
     *
     * @param smsDTO       短信配置dto
     * @param phone        手机号
     * @param randomNumber 验证码
     * @return sendSmsDTO
     */
    private ISendSmsDTO getSendSmsDTO(SmsDTO smsDTO, String phone, String randomNumber) {
        ISendSmsDTO sendSmsDTO;
        switch (smsDTO.getId()) {
            case EipsConfigConsts.SDCZ:
                sendSmsDTO = SDCZSendSmsDTO.builder()
                        .CorpID(smsDTO.getAccount())
                        .Pwd(smsDTO.getPassword())
                        .Mobile(phone)
                        .content(String.format(smsDTO.getContent(), randomNumber))
                        .build();
                break;
            case EipsConfigConsts.ZWX:
                sendSmsDTO = ZWXSendSmsDTO.builder()
                        .apName(smsDTO.getAccount())
                        .apPassword(smsDTO.getPassword())
                        .calledNumber(phone)
                        .content(String.format(smsDTO.getContent(), randomNumber))
                        .build();
                break;
            default:
                sendSmsDTO = null;
                break;
        }
        return sendSmsDTO;
    }

    /**
     * 因使用的仍是NameValuePair，故进行转换
     *
     * @param sendSmsDTO sendSmsDTO
     * @return pairs
     */
    private List<NameValuePair> getNameValuePairs(ISendSmsDTO sendSmsDTO) {
        List<NameValuePair> pairs = new ArrayList<>();
        Field[] fields = sendSmsDTO.getClass().getDeclaredFields();
        for (Field field : fields) {
            // 获取属性名
            String name = field.getName();
            // 获取属性值
            String value = ReflectUtil.get(sendSmsDTO, name);
            pairs.add(new BasicNameValuePair(name, value));
        }
        return pairs;
    }
}
